package _.mail.javax;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.mail.BodyPart;
import javax.mail.Flags;
import javax.mail.Header;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.internet.MimeMessage;

import org.budo.mail.javax.MailAttachment;
import org.budo.mail.javax.ReceiveMail;
import org.budo.mail.util.MessageUtil;
import org.budo.support.java.collection.wrapper.NiceToStringMapWrapper;
import org.budo.support.lang.util.BooleanUtil;
import org.budo.support.lang.util.ListUtil;
import org.budo.support.lang.util.MapUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.slf4j.Slf4j;
import org.slf4j.Logger;

import _.javax.mail.internet.util.MimeMessageUtil;
import _.mail.MailImpl;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
 * @author li
 * @version 1 (2014年7月8日 下午12:52:40)
 * @since Java7
 */
@Getter
@Setter
@NoArgsConstructor
public class ReceiveMailImpl extends MailImpl implements ReceiveMail {
    private static final long serialVersionUID = 1416126665880739848L;

    private static final Logger log = Slf4j.getLogger();

    private Message message;

    private String uid;

    private List<MailAttachment> attachments;

    /**
     * 标记是否删除, 防止多次删除操作
     */
    private Boolean deleted = false;

    private Boolean seened = false;

    private List<String> toAddresses;

    private List<String> ccAddresses;

    private List<String> bccAddresses;

    public ReceiveMailImpl(Message message) {
        this.message = message;
    }

    /**
     * 取第一个
     */
    @Override
    public String getFrom() {
        if (null == super.getFrom() && null != this.getMessage()) {
            List<String> messageFroms = MessageUtil.getFrom(this.getMessage());
            String messageFrom = ListUtil.first(messageFroms);
            super.setFrom(messageFrom);
        }

        return super.getFrom();
    }

    /**
     * 取多个
     */
    @Override
    public List<String> getBccAddresses() {
        if (null == this.bccAddresses && null != this.getMessage()) {
            List<String> bccAddresses = MessageUtil.getBcc(this.getMessage());
            this.bccAddresses = bccAddresses;
        }

        return this.bccAddresses;
    }

    @Override
    public List<String> getCcAddresses() {
        if (null == this.ccAddresses && null != this.getMessage()) {
            List<String> ccAddresses = MessageUtil.getCc(this.getMessage());
            this.ccAddresses = ccAddresses;
        }

        return this.ccAddresses;
    }

    @Override
    public String getCc() {
        if (null == super.getCc() && null != this.getMessage()) {
            List<String> cc = MessageUtil.getCc(this.getMessage());
            super.setCc(StringUtil.join(cc));
        }

        return super.getCc();
    }

    @Override
    public String getBcc() {
        if (null == super.getBcc() && null != this.getMessage()) {
            List<String> bcc = MessageUtil.getBcc(this.getMessage());
            super.setBcc(StringUtil.join(bcc));
        }

        return super.getBcc();
    }

    @Override
    public List<String> getToAddresses() {
        if (null == this.toAddresses && null != this.getMessage()) {
            List<String> toAddresses = MessageUtil.getTo(this.getMessage());
            this.toAddresses = toAddresses;
        }

        return this.toAddresses;
    }

    /**
     * 收件人多个的时候, 返回了第一个
     */
    @Override
    public String getTo() {
        if (null == super.getTo() && null != this.getMessage()) {
            List<String> toAddresses = MessageUtil.getTo(this.getMessage());
            String firstToAddress = ListUtil.first(toAddresses);
            super.setTo(firstToAddress);
        }

        return super.getTo();
    }

    @Override
    public String getSubject() {
        if (null == super.getSubject() && null != this.getMessage()) {
            String subject = ReceiveMailUtil.getMailSubject(this.getMessage());
            super.setSubject(subject);
        }

        return super.getSubject();
    }

    @Override
    public String getContent() {
        if (null == super.getContent() && null != this.getMessage()) {
            String htmlMailContent = ReceiveMailUtil.getHtmlMailContent(this.getMessage());
            super.setContent(htmlMailContent);
        }

        return super.getContent();
    }

    @Override
    public List<MailAttachment> getAttachments() {
        if (null == this.attachments) {
            this.attachments = MessageUtil.getAttachments(this.getMessage());
        }

        return this.attachments;
    }

    @Override
    public String getUid() {
        if (null == this.uid) {
            this.uid = MessageUtil.getUid(this.getMessage());
        }

        return this.uid;
    }

    @Override
    public String getMessageId() {
        if (this.getMessage() instanceof MimeMessage) {
            return MimeMessageUtil.getMessageId((MimeMessage) this.getMessage());
        }

        log.error("#184 this.getMessageId() 失败 message 类型不是 MimeMessage " + this.getMessage().getClass());
        return "message-is-not-mime-message";
    }

    @Override
    public Date getSentDate() {
        if (this.getMessage() instanceof MimeMessage) {
            return MimeMessageUtil.getSentDate((MimeMessage) this.getMessage());
        }

        log.error("#193 getSentDate() 失败 message 类型不是 MimeMessage " + this.getMessage().getClass());
        return null;
    }

    @Override
    public Date getReceivedDate() {
        if (this.getMessage() instanceof MimeMessage) {
            return MimeMessageUtil.getReceivedDate((MimeMessage) this.getMessage());
        }

        log.error("#201 getReceivedDate() 失败 message 类型不是 MimeMessage " + this.getMessage().getClass());
        return null;
    }

    @Override
    public String getSender() {
        if (this.getMessage() instanceof MimeMessage) {
            return MimeMessageUtil.getSender((MimeMessage) this.getMessage());
        }

        log.error("#311 getReceivedDate() 失败 message 类型不是 MimeMessage " + this.getMessage().getClass());
        return "message-is-not-mime-message";
    }

    @Override
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public Map<String, List<String>> getHeaders() {
        try {
            Map<String, List<String>> allHeaders = new HashMap<String, List<String>>();
            Enumeration enumeration = this.getMessage().getAllHeaders();

            while (enumeration.hasMoreElements()) {
                Header header = (Header) enumeration.nextElement();
                String key = header.getName();
                List<String> values = allHeaders.get(key);
                if (null == values) {
                    allHeaders.put(key, values = new ArrayList<String>());
                }
                values.add(header.getValue());
            }

            return new NiceToStringMapWrapper(allHeaders);
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param delimiter 分隔符
     */
    public String getHeader(String headerName, String delimiter) {
        List<String> headerValues = this.getHeaders(headerName);

        if (null == headerValues) {
            return null;
        }

        if ((headerValues.size() == 1) || delimiter == null) {
            return headerValues.get(0);
        }

        StringBuffer stringBuffer = new StringBuffer(headerValues.get(0));
        for (int i = 1; i < headerValues.size(); i++) {
            stringBuffer.append(delimiter);
            String val = headerValues.get(i);
            stringBuffer.append(val);
        }

        return stringBuffer.toString();
    }

    @Override
    public List<String> getHeaders(String key) {
        try {
            String[] headers = this.getMessage().getHeader(key);
            return ListUtil.arrayToList(headers);
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String getHeader(String key) {
        List<String> headers = this.getHeaders(key);
        return ListUtil.first(headers);
    }

    public Map<String, Object> messageToMap() {
        try {
            Message message = this.getMessage();

            return MapUtil.stringObjectMap("message", this.messageToMap(message));
        } catch (Throwable e) {
            log.error("#374 messageToMap error, e=" + e, e);
            return null;
        }
    }

    /**
     * @see _.javax.mail.util.MessageUtil_2#getImagesInMessageBody_新版本(Message)
     */
    private Map<String, Object> messageToMap(Object message) throws IOException, MessagingException {
        if (null == message) {
            return MapUtil.stringObjectMap("error", "message_is_null");
        }

        Map<String, Object> map = new HashMap<String, Object>();
        if (message instanceof Message) {
            Map<String, Object> headerMap = this.headerMap((Message) message);

            Object content = ((Message) message).getContent();
            map.put("javax.mail.Message", MapUtil.stringObjectMap("type", message.getClass().getName(), //
                    "content", this.messageToMap(content), "headerMap", headerMap));
            return map;
        }

        if (message instanceof Multipart) {
            Multipart multipart = (Multipart) message;
            int multipartCount = multipart.getCount();
            if (multipartCount < 1) {
                map.put("javax.mail.Multipart", MapUtil.stringObjectMap("type", message.getClass().getName(), //
                        "error", "multipart_count_less_than_1"));
            }

            Map<String, Object> bodyParts = new HashMap<String, Object>();
            for (int i = 0; i < multipartCount; i++) {
                BodyPart bodyPart = multipart.getBodyPart(i);
                bodyParts.put("bodyPart-" + i, this.messageToMap(bodyPart));
            }

            map.put("javax.mail.Multipart", MapUtil.stringObjectMap("type", message.getClass().getName(), //
                    "bodyParts", bodyParts));
            return map;
        }

        if (message instanceof BodyPart) {
            Object content = ((BodyPart) message).getContent();
            Map<String, Object> headerMap = this.headerMap((BodyPart) message);
            map.put("javax.mail.BodyPart", MapUtil.stringObjectMap("type", message.getClass().getName(), //
                    "content", this.messageToMap(content), //
                    "headerMap", headerMap));
            return map;
        }

        if (message instanceof InputStream) {
            Map<String, Object> inputStreamMap = MapUtil.stringObjectMap("type", message.getClass().getName(), //
                    "available", ((InputStream) message).available());
            map.put("java.io.InputStream", inputStreamMap);
            return map;
        }

        if (message instanceof String) {
            map.put("java.lang.String", StringUtil.cutLeft((String) message, 50, "..."));
            return map;
        }

        map.put("else", MapUtil.stringObjectMap("message_type_is", message.getClass().getName()));
        return map;
    }

    private Map<String, Object> headerMap(Part message) throws MessagingException {
        Enumeration<?> enumeration = message.getAllHeaders();
        Map<String, Object> headerMap = new HashMap<String, Object>();

        while (enumeration.hasMoreElements()) {
            Header header = (Header) enumeration.nextElement();
            headerMap.put(header.getName(), header.getValue());
        }

        return headerMap;
    }

    /**
     * 将邮件设置删除标记,需要配合 folder.open(Folder.READ_WRITE) 及 folder.close(true)才会真的删除
     */
    public Boolean delete() {
        if (BooleanUtil.isTrue(this.deleted)) {
            log.warn("#337 delete again? " + ", message=" + this.getMessage() + ", messageId=" + getMessageId());
            return true;
        }

        try {
            log.info("#391 setting delete flag message=" + this.getMessage() + ", messageId=" + getMessageId());
            this.getMessage().setFlag(Flags.Flag.DELETED, true);
            // this.getMessage().saveChanges();//javax.mail.IllegalWriteException:POP3messagesareread-only

            this.deleted = true;
            return true;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Boolean seen() {
        if (BooleanUtil.isTrue(this.seened)) {
            log.warn("#258 seened again? " + ", message=" + this.getMessage() + ", messageId=" + getMessageId());
            return true;
        }

        try {
            log.info("#363 setting seened flag message=" + this.getMessage() + ", messageId=" + getMessageId());
            this.getMessage().setFlag(Flags.Flag.SEEN, true);
            // this.getMessage().saveChanges();//javax.mail.IllegalWriteException:POP3messagesareread-only

            this.seened = true;
            return true;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public String toString() {
        return super.toString() //
                + ", subject=" + this.getSubject() //
                + ", from=" + this.getFrom() //
                + ", toAddresses=" + this.getToAddresses() //
                + ", ccAddresses=" + this.getCcAddresses() //
                + ", messageId=" + this.getMessageId();
    }
}